# pylint: disable-msg=E1101,E0611,F0401
"""
@author: Sergiy Tsymbalyuk
"""

from getpass import getpass
from hashlib import md5
from datetime import datetime, timedelta

from pylons import config
from zope.interface import implements
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
from repoze.who.interfaces import IAuthenticator
from repoze.what.plugins.quickstart import setup_sql_auth

from chowa.lib.base import Session
from chowa.model.auth import *
from chowa.model.abstract import Person


jcuken = {
    u'\u0406': 'S', u'\u0407': '}', u'\u0456': 's', u'\u0457': ']',
    
    u'\u0410': 'F', u'\u0411': '<', u'\u0412': 'D', u'\u0413': 'U',
    u'\u0414': 'L', u'\u0415': 'T', u'\u0416': ':', u'\u0417': 'P',
    u'\u0418': 'B', u'\u0419': 'Q', u'\u041A': 'R', u'\u041B': 'K',
    u'\u041C': 'V', u'\u041D': 'Y', u'\u041E': 'J', u'\u041F': 'G',
    u'\u0420': 'H', u'\u0421': 'C', u'\u0422': 'N', u'\u0423': 'E',
    u'\u0424': 'A', u'\u0425': '{', u'\u0426': 'W', u'\u0427': 'X',
    u'\u0428': 'I', u'\u0429': 'O', u'\u042A': '}', u'\u042B': 'S',
    u'\u042C': 'M', u'\u042D': '"', u'\u042E': '>', u'\u042F': 'Z',
    
    u'\u0430': 'f', u'\u0431': ',', u'\u0432': 'd', u'\u0433': 'u',
    u'\u0434': 'l', u'\u0435': 't', u'\u0436': ';', u'\u0437': 'p',
    u'\u0438': 'b', u'\u0439': 'q', u'\u043A': 'r', u'\u043B': 'k',
    u'\u043C': 'v', u'\u043D': 'y', u'\u043E': 'j', u'\u043F': 'g',
    u'\u0440': 'h', u'\u0441': 'c', u'\u0442': 'n', u'\u0443': 'e',
    u'\u0444': 'a', u'\u0445': '[', u'\u0446': 'w', u'\u0447': 'x',
    u'\u0448': 'i', u'\u0449': 'o', u'\u044A': ']', u'\u044B': 's',
    u'\u044C': 'm', u'\u044D': "'", u'\u044E': '.', u'\u044F': 'z',
}

def convert_layout(cyr_str):
    ansi_str = ''
    for c in cyr_str:
        nc = c
        if nc in jcuken:
            nc = jcuken[c]
        ansi_str += nc
    return ansi_str


class NameAuthenticator():
    """repoze.who person name authenticator."""
    
    implements(IAuthenticator)
    
    def __init__(self):
        pass
    
    def authenticate(self, environ, identity):
        if not ('login' in identity and 'password' in identity):
            return None
        
        query = Session.query(User)
        tokens = identity['login'].split(' ')
        name_query = query.join(Person).filter(Person.surname==tokens[0])
        if len(tokens) == 2:
            name_query = name_query.filter(Person.name==tokens[1])
        elif len(tokens) > 2:
            return None
        
        try:
            user = name_query.one()
            if user.is_disabled:
                return None
            password = convert_layout(identity['password'])
            if user.validate_password(password):
                return user.username
            else:
                return None
        except(NoResultFound, MultipleResultsFound):
            return None


def add_auth(app):
    """Add authentication middleware.""" 
    translations = {
        'user_name': 'username',
        'group_name': 'name',
        'permission_name': 'name',
    }
    return setup_sql_auth(app, User, Group, Permission, Session,
        login_url='http:/site/auth/login',
        post_login_url='/welcome',
        post_logout_url='/logout',
        charset='utf8',
        translations=translations,
        authenticators=[('chowa_auth', NameAuthenticator())])


def add_admin(admin_name='admin', admin_group_name='administrators'):
    """Add 'admin' user."""
    query = Session.query(User).filter_by(username=admin_name)
    admin = None
    if query.count() == 0:
        password = ''
        while 1:
            password = getpass("Enter password for user '%s': " % admin_name)
            try:
                hashed_pass = md5(password).hexdigest()
                break
            except:
                pass
        admin = User(username=admin_name, _password=hashed_pass)
        Session.add(admin)
        Session.commit()
    elif query.count() == 1:
        admin = query.one()
    
    query = Session.query(Group).filter_by(name=admin_group_name)
    admins = None
    if query.count() == 0:
        admins = Group(name=admin_group_name)
        Session.add(admins)
        Session.commit()
    elif query.count() == 1:
        admins = query.one()
    
    query = Session.query(user_groups).filter(User.id==admin.id) \
                                      .filter(Group.id==admins.id)
    if query.count() == 0:
        admin_group = user_groups.insert(values=dict(user_id=admin.id,
                                                     group_id=admins.id))
        Session.execute(admin_group)


def add_system(system_name='system'):
    query = Session.query(User).filter_by(username=system_name)
    if query.count() == 0:
        system = User(username=system_name)
        Session.add(system)
        Session.commit()


def add_permissions(permissions, session):
    for permission in permissions:
        count = session.query(Permission).filter_by(name=permission).count()
        if count == 0:
            p = Permission(name=permission)
            session.add(p)
    session.commit()


def add_groups(groups, session):
    for (group, permissions) in groups:
        try:
            g = session.query(Group).filter_by(name=group).one()
        except:
            g = Group(name=group)
            session.add(g)
            session.commit()
        for permission in permissions:
            try:
                p = session.query(Permission).filter_by(name=permission).one()
            except:
                p = Permission(name=permission)
                session.add(p)
                session.commit()
            add_gp = group_permissions.insert(values=dict(group_id=g.id,
                                                          permission_id=p.id))
            try:
                session.execute(add_gp)
            except:
                pass


class UserActivityMiddleware(object):
    def __init__(self, app):
        self.app = app
        
    def __call__(self, environ, start_response):
        now = datetime.now()
        critical_time = now - timedelta(minutes=5)
        try:
            query = Session.query(User)
            query = query.filter(User.last_activity < critical_time)
            query.update({'is_online': False})
            Session.commit()
        except:
            Session.rollback()
        identity = environ.get('repoze.who.identity')
        user_id = None
        if identity:
            user = identity['user']
            try:
                user.is_online = True
                user.last_activity = now
                Session.add(user)
                Session.commit()
            except:
                Session.rollback()
            user_id = user.id
        ip = environ.get('REMOTE_ADDR')
        agent = environ.get('HTTP_USER_AGENT')
        try:
            visit = Session.query(Visit).filter_by(date=now.date(), ipv4=ip).first()
            if visit:
                visit.user_agent = agent
                visit.last_activity = now
                visit.user_id = user_id
            else:
                visit = Visit(ipv4=ip, user_agent=agent,
                              date=now.date(),
                              last_activity=now,
                              first_activity=now,
                              user_id=user_id)
            Session.add(visit)
            Session.commit()
        except:
            Session.rollback()
        return self.app(environ, start_response)
